package com.dhy.qigsawsplitdownloader

import android.content.Context
import com.liulishuo.okdownload.OkDownload
import com.liulishuo.okdownload.core.connection.DownloadOkHttp3Connection
import okhttp3.*
import okio.buffer
import okio.source
import java.io.File

/**
 * 新安装时，先从缓存中找MD5相同的apk。
 * */
object SplitCacheUtil {
    /**
     * 需要在Application.onCreate 中调用，因为OkDownload使用了OkDownloadProvider 做初始化。
     * */
    fun initOkDownload(context: Context) {
        val cf = DownloadOkHttp3Connection.Factory()
        cf.builder().addInterceptor {
            it.request().cacheResponse(context) ?: it.proceed(it.request())
        }
        val builder = OkDownload.Builder(context).connectionFactory(cf)
        OkDownload.setSingletonInstance(builder.build())
    }

    private fun Request.cacheResponse(context: Context): Response? {
        val url = url.toString()
        if (url.startsWith("http")) {
            val cacheReg = "-([a-z0-9]{32}\\.apk$)".toRegex()//'md5.apk'
            val cacheName = cacheReg.find(url)?.groupValues?.get(1)
            if (cacheName != null) {
                val cache = context.splitsDir().find(cacheName)
                context.clearCacheSplits(cache)
                return if (cache?.exists() == true) toResponse(cache) else null
            }
        }
        return null
    }

    private const val MAX_SPLIT_CACHE_COUNT = 2

    /**
     * 每个类别只保存两个文件，方便快速回退到上一插件版本。
     * 因为是在下载前清理缓存，所以可能出现清理后又下载了一个，即临时多了一个。多出的会在下载其它项时清理掉。
     * 下载前清理错了也没关系，随后会重新下载，但下载后清理却不能。
     * */
    private fun Context.clearCacheSplits(using: File?) {
        splitsDir().listFiles()?.forEach {
            val splits = it.listFiles() ?: return@forEach
            if (splits.size > MAX_SPLIT_CACHE_COUNT) {
                splits.sortByDescending { f -> f.lastModified() }
                for ((index, split) in splits.withIndex()) {
                    if (split != using && index >= MAX_SPLIT_CACHE_COUNT) split.delete()
                }
            }
        }
    }

    private fun File.find(fileName: String): File? {
        val file = this
        if (file.isDirectory) {
            file.listFiles()?.onEach {
                val f = it.find(fileName)
                if (f != null) return f
            }
        } else {
            if (file.name == fileName) return file
        }
        return null
    }
}

private fun Request.toResponse(file: File): Response {
    return Response.Builder()
        .code(200)
        .message("file response")
        .protocol(Protocol.HTTP_1_0)
        .request(this)
        .body(file.asResponseBody())
        .build()
}

private fun File.asResponseBody(contentType: MediaType? = null): ResponseBody {
    val fileSource = source().buffer()
    return object : ResponseBody() {
        override fun contentType() = contentType
        override fun contentLength() = length()
        override fun source() = fileSource
    }
}

/**
 * 分类存储：/files/QgsawSplits/{插件名}/{md5.apk} => /files/QgsawSplits/java-master.apk/2f2f94400cd0fbabb2e1352a1dbc8cdd.apk
 * */
fun Context.splitsDir(): File {
    return File(filesDir, "QgsawSplits")
}